//
//  SEPlanet.cpp
//  Space Explorer
//
//  Created by Yann Blaudin de Thé on 05/04/13.
//  Copyright (c) 2013 TeaTime. All rights reserved.
//

#include "SESpiceManager.h"
#include "SEPlanet.h"


SEPlanet::SEPlanet(irr::IrrlichtDevice *device, SEPlanet* attractor, const irr::core::stringw& disp_name, const std::string& spice_name, irr::video::ITexture* ground_tex, irr::video::ITexture* cloud_tex, irr::video::ITexture* normal_tex) {
    _irr_device = device;
    
    _attractor = attractor;
    
    _nom_aff = disp_name;
    _nom_spice = spice_name;
    
    _spice_object = SESpice::Object(_nom_spice);
    
    _spice_object.getRadii(&_Req, &_Rpo);
    _GM = _spice_object.getGravitaionalParameter();
        
    std::cout << "Rayon equatorial " << spice_name << " : " << _Req << "km" << std::endl;
    
    irr::scene::ISceneManager *smgr = device->getSceneManager();
    std::string planet_mesh = GetApplicationResourcesPath() + "earth.x";
    _irr_ground_node = smgr->addMeshSceneNode(smgr->getMesh(planet_mesh.c_str()));
    _irr_ground_node->setScale(vec3f(_Req, _Req, _Rpo));
    
    if (spice_name == "Sun") {
        _irr_ground_node->setMaterialFlag(irr::video::EMF_LIGHTING, false);
        _irr_ground_node->setVisible(false);
    }
    
    _irr_ground_node->setMaterialTexture(0, ground_tex);

    if (normal_tex) {
        _irr_ground_node->setMaterialTexture(1, normal_tex);
        _irr_ground_node->setMaterialType(irr::video::EMT_NORMAL_MAP_SOLID );
        _irr_ground_node->getMaterial(1).MaterialTypeParam = 1.f / 64.f;
    }
    
}

SEPlanet::~SEPlanet() {
    
}

void SEPlanet::update(SEClock &ck, const vec3d& cam_pos) {
    
    _position = _spice_object.getPosition(ck.getSimTime());
    
    _velocity = _spice_object.getVelocity(ck.getSimTime());
    
    
    ////////////////////////////////////////////////////////
    //          Translation
    ////////////////////////////////////////////////////////
    vec3f irr_pos;
    vec3d ray;
    
    ray = _position - cam_pos;
 
    irr_pos.X = ray.X;
    irr_pos.Y = ray.Y;
    irr_pos.Z = ray.Z;

    _irr_ground_node->setPosition(irr_pos);
    
    
    ////////////////////////////////////////////////////////
    //          Rotation
    ////////////////////////////////////////////////////////
    mat4f att = _spice_object.getAttitude(ck.getSimTime());
    
//    vec3f rot2(ck.getSimDuration()*10,0,0);
//    att.setRotationDegrees(rot2);
    mat4f att_conv;
    float coeff[16] = {0,-1,0,0, 0,0,-1,0, 1,0,0,0, 0,0,0,1};
    
    att_conv.setM(coeff);
    mat4f irr_att = att*att_conv;
    
    vec3f rot = irr_att.getRotationDegrees();
    _irr_ground_node->setRotation(rot);
    
    
    ////////////////////////////////////////////////////////
    //          Parametres orbitaux
    ////////////////////////////////////////////////////////
    if (_attractor ) {
        btVector3 pos_j2000(_position.X, _position.Y, _position.Z);
        btVector3 vit_j2000(_velocity.X, _velocity.Y, _velocity.Z);
        get_orbital_elmnt(0, _attractor->getGravitationalParameter(), pos_j2000, vit_j2000, &_orb_elem);
    }
    
}

SpiceDouble SEPlanet::getPolarRadius() const {
    
    return _Rpo;
    
}

SpiceDouble SEPlanet::getEquatorialRadius() const {
    
    return _Req;
}

vec3f SEPlanet::getIrrPosition() const {
    return _irr_ground_node->getPosition();
}

vec3d SEPlanet::getPosition() const {
    return _position;
}

vec3d SEPlanet::geocg2geod(vec3d pos) {
    irr::f64 lat, lon, alt;
    irr::f64 e2, slat2, lat_prec;
    
    e2 = 1 - pow(_Rpo/_Req,2);
    
    lon = atan(pos.Y/pos.X);
    lat = atan(pos.Z/(sqrt(pow(pos.X,2)+pow(pos.Y,2))*(1-_Req*e2/pos.getLength())));
    
    for (int i=0; i<6; i++) {
        lat_prec = lat;
        slat2 = pow(sin(lat),2);
        lat = atan(pos.Z/(sqrt(pow(pos.X,2)+pow(pos.Y,2))*(1-_Req*e2*cos(lat)/(sqrt(pow(pos.X,2)+pow(pos.Y,2))*sqrt(1-e2*slat2)))));
    }
    
    slat2 = pow(sin(lat),2);
    alt = sqrt(pow(pos.X,2)+pow(pos.Y,2))/cos(lat) - _Req/sqrt(1-e2*slat2);
    
    return vec3d(lat,lon,alt);
    
}

vec3d SEPlanet::geod2geocg(irr::f32 lat, irr::f32 lon, irr::f32 alt) {
    vec3d pos;
    irr::f32 N, e, slat;
    
    slat = sin(lat);
    
    e = sqrt(1 - pow(_Rpo/_Req,2));
    
    N = _Req/sqrt(1 - e*e*slat*slat);
    
    pos.X = (N + alt)*cos(lat)*cos(lon);
    pos.Y = (N + alt)*cos(lat)*sin(lon);
    pos.Z = (N*(1-e*e) + alt)*sin(lat);
    
    return pos;
    
}

irr::scene::IMeshSceneNode* SEPlanet::getIrrNode() {
    return _irr_ground_node;
}

SEOrbitalElements* SEPlanet::getOrbitalElements() {
    return &_orb_elem;
}

std::string SEPlanet::getName() const {
    std::string res;
    char* txt;
    const wchar_t* nom = _nom_aff.c_str();
    bool cont;
    
    int lon = 0;
    cont = true;
    while (nom[lon++] != '\0') {}
    
    txt = (char*)malloc(lon);
    for (int i=0; i<lon; i++)
        txt[i] = (char)nom[i];
    
    res = std::string(txt);
    free(txt);
    
    return res;
}

SpiceDouble SEPlanet::getGravitationalParameter() const {
    return _GM;
}

SpiceDouble SEPlanet::getSphereOfInfluence() const {
    return _orb_elem.semiMajorAxis*pow(_GM/_attractor->getGravitationalParameter(),0.4);
}






